package com.sjsu.cmpe239.evaluationTests;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.common.Weighting;
import org.apache.mahout.cf.taste.eval.IRStatistics;
import org.apache.mahout.cf.taste.eval.RecommenderBuilder;
import org.apache.mahout.cf.taste.eval.RecommenderEvaluator;
import org.apache.mahout.cf.taste.eval.RecommenderIRStatsEvaluator;
import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.eval.GenericRecommenderIRStatsEvaluator;
import org.apache.mahout.cf.taste.impl.eval.RMSRecommenderEvaluator;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.model.jdbc.MySQLJDBCDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommender;
import org.apache.mahout.cf.taste.impl.recommender.svd.ALSWRFactorizer;
import org.apache.mahout.cf.taste.impl.recommender.svd.SVDRecommender;
import org.apache.mahout.cf.taste.impl.similarity.CachingUserSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.SpearmanCorrelationSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.TanimotoCoefficientSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.model.JDBCDataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.junit.BeforeClass;
import org.junit.Test;

import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

public class UserBasedRecommenderEvalatorTest {
	// TODO Auto-generated method stub
	// static DataModel model = buildDataModel();
	static FileDataModel model;

	@BeforeClass
	public static/* DataModel */void buildDataModel() throws IOException {
		MysqlDataSource dataSource = new MysqlConnectionPoolDataSource();
		dataSource.setServerName("localhost");
		dataSource.setUser("root");
		dataSource.setPassword("");
		dataSource.setDatabaseName("239db");

		model = new FileDataModel(new File("d:/ratings.csv"));


	}

	/*
	 * UserSimilarity : PearsonCorrelationSimilarity
	 * UserNeighborhood:NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithPearson() throws TasteException {

		// Start: evaluate with Average absolute difference
		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new PearsonCorrelationSimilarity(
						model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};

		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("\nevaluateUserBasedRecWithPearson");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		// Start: evaluate with RMSRecommenderEvaluator
		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("RMSEvaluator score: " + score);

	}

	/*
	 * UserSimilarity : PearsonCorrelationSimilarity,weighted
	 * UserNeighborhood:NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithPearsonAndWeighting()
			throws TasteException {

		// Start: evaluate with Average absolute difference
		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new PearsonCorrelationSimilarity(
						model, Weighting.WEIGHTED);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};

		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("\nevaluateUserBasedRecWithPearson");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		// Start: evaluate with RMSRecommenderEvaluator
		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("RMSEvaluator score: " + score);

		/*
		 * RecommenderIRStatsEvaluator irStatsEvaluator = new
		 * GenericRecommenderIRStatsEvaluator(); IRStatistics stats =
		 * irStatsEvaluator.evaluate(builder, null, model, null, 2, 1.0, 1.0);
		 * System.out.println("IRStatistics precision: " +
		 * stats.getPrecision()); System.out.println("IRStatistics recall: " +
		 * stats.getRecall());
		 */

	}

	/*
	 * UserSimilarity : EuclideanDistanceSimilarity, UserNeighborhood:
	 * NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithEuclidean() throws TasteException { // evaluate
																			// with
																			// Average
																			// absolute
																			// difference

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				System.out.println("2");
				UserSimilarity similarity = new EuclideanDistanceSimilarity(
						model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};
		long start = System.currentTimeMillis();
		System.out.println("start time: " + start);
		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		long end = System.currentTimeMillis();
		System.out.println("end time: " + end);
		System.out.println("total time: " + (end - start) + "ms");
		System.out.println("\nevaluateUserBasedRecWithEuclidean");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		evaluator = new RMSRecommenderEvaluator(); // evaluate with
													// RMSRecommenderEvaluator
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("RMSEvaluator score: " + score);
	}

	/*
	 * UserSimilarity : EuclideanDistanceSimilarity,weighted UserNeighborhood:
	 * NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithEuclideanWithWeighting()
			throws TasteException { // evaluate with Average absolute difference

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new EuclideanDistanceSimilarity(
						model, Weighting.WEIGHTED);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};
		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("\n evaluateUserBasedRecWithEuclideanWithWeighting");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		evaluator = new RMSRecommenderEvaluator(); // evaluate with
													// RMSRecommenderEvaluator
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);
		System.out.println("RMSEvaluator score: " + score);
	}

	/*
	 * UserSimilarity : SpearmanCorrelationSimilarity, UserNeighborhood:
	 * NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithSpearman() throws TasteException {

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new SpearmanCorrelationSimilarity(
						model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};
		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0); // evaluate
																			// with
																			// Average
																			// absolute
																			// difference
		System.out.println("\n evaluateUserBasedRecWithSpearman");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		// /
		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);// evaluate
																	// with
																	// RMSRecommenderEvaluator
		System.out.println("RMSEvaluator score: " + score);
	}

	/*
	 * UserSimilarity : SpearmanCorrelationSimilarity,CachingUserSimilarity
	 * UserNeighborhood: NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithCachingSim() throws TasteException {

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new SpearmanCorrelationSimilarity(
						model);
				UserSimilarity cachingUserSimilarity = new CachingUserSimilarity(
						similarity, model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						cachingUserSimilarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};
		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0); // evaluate
																			// with
																			// Average
																			// absolute
																			// difference
		System.out
				.println("\n SpearmanCorrelationSimilarity,CachingUserSimilarity");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);// evaluate
																	// with
																	// RMSRecommenderEvaluator
		System.out.println("RMSEvaluator score: " + score);
	}

	/*
	 * UserSimilarity : TanimotoCoefficientSimilarity, UserNeighborhood:
	 * NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithTanimoto() throws TasteException {

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new TanimotoCoefficientSimilarity(
						model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);
				return new GenericUserBasedRecommender(model, neighborhood,
						similarity);
			}
		};
		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0); // evaluate
																			// with
																			// Average
																			// absolute
																			// difference
		System.out
				.println("======evaluateUserBasedRecWithTanimoto==============================================");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		// /
		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);// evaluate
																	// with
																	// RMSRecommenderEvaluator
		System.out.println("RMSEvaluator score: " + score);
	}

	/*
	 * UserSimilarity : LogLikelihoodSimilarity UserNeighborhood:
	 * NearestNUserNeighborhood
	 */
	@Test
	public void evaluateUserBasedRecWithLogLikelihoodSimilarity()
			throws TasteException {

		RecommenderEvaluator evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator();
		RecommenderBuilder builder = new RecommenderBuilder() {
			@Override
			public Recommender buildRecommender(DataModel model)
					throws TasteException {
				UserSimilarity similarity = new LogLikelihoodSimilarity(model);
				UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
						similarity, model);

				Recommender r = new GenericUserBasedRecommender(model,
						neighborhood, similarity);
				// List<RecommendedItem> recommendations = r.recommend(3, 10);
				// System.out.println("top 10 recommendations for user 3 :");
				// for (RecommendedItem recommendation : recommendations) {
				// System.out.println(recommendation);
				// }
				return r;
			}
		};

		double score = evaluator.evaluate(builder, null, model, 0.9, 1.0); // evaluate
																			// with
																			// Average
																			// absolute
																			// difference
		System.out
				.println("====evaluateUserBasedRecWithLogLikelihoodSimilarity=====================================================");
		System.out
				.println("AverageAbsoluteDifferenceEvaluator score: " + score);

		// /
		evaluator = new RMSRecommenderEvaluator();
		score = evaluator.evaluate(builder, null, model, 0.9, 1.0);// evaluate
																	// with
																	// RMSRecommenderEvaluator
		System.out.println("RMSEvaluator score: " + score);
	}

	@Test
	public void dummy() throws TasteException {
		UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
		UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
				similarity, model);
		GenericUserBasedRecommender r = new GenericUserBasedRecommender(model,
				neighborhood, similarity);

		long[] users = r.mostSimilarUserIDs(5, 10);
		for (int i = 0; i < users.length; i++) {
			System.out.println(users[i] + ",");
		}
	}

	@Test
	public void testwithMultipleThreads() throws TasteException,
			InterruptedException {
		UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
		UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
				similarity, model);
		final GenericUserBasedRecommender r = new GenericUserBasedRecommender(
				model, neighborhood, similarity);

		List<Thread> usersList = new ArrayList<Thread>();
		final AtomicInteger userId = new AtomicInteger();
		userId.set(0);
		for (int i = 0; i < 3; i++) {
			usersList.add((new Thread() {
				public void run() {
					List<RecommendedItem> items;
					try {
						int userId1 = userId.incrementAndGet();
						items = r.recommend(userId1, 3);
						System.out.println("UsrId1: " + userId1);
						for (int i = 0; i < items.size(); i++) {
							System.out.println(items.get(i) + ",");
						}

					} catch (TasteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}));
			// usersList.get(i).start();

		}
		for (int i = 0; i < 3; i++) {
			usersList.get(i).start();
		}
		/*
		 * for(int i=0;i<usersList.size();i++){ usersList.get(i).wait(); }
		 */
	}

	/*@Test
	public void finalRecommenderTest() throws TasteException,
			InterruptedException {
		UserSimilarity similarity = new PearsonCorrelationSimilarity(model);
		UserNeighborhood neighborhood = new NearestNUserNeighborhood(2,
				similarity, model);
		final GenericUserBasedRecommender r = new GenericUserBasedRecommender(
				model, neighborhood, similarity);

		List<Thread> usersList = new ArrayList<Thread>();
		List<RecommendedItem> items;
		Recommender239 rec =new Recommender239();
		items = rec.movieRecommendations(1);
		
		for (int i = 0; i < items.size(); i++) {
			System.out.println(items.get(i) + ",");
		}

	}*/

	
}
